<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>SurfaceView 与 TextureView 详解 | wiki小站 生活-代码之道</title>
  <meta name="keywords" content=" 安卓开发 ">
  <meta name="description" content="SurfaceView 与 TextureView 详解 | wiki小站 生活-代码之道">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="description" content="前言播放视频或者渲染其他的动画的时候，有两个 View 组件可供选择，SurfaceView 和 TextureView，GLSurfaceView 是 SurfaceView 的子类，在 SurfaceView 基础上封装了 EGL 环境管理以及 Render 线程，专用于 3D 游戏开发的视图，这里归类到 SurfaceView 中。在介绍 SurfaceView 与 TextureView">
<meta property="og:type" content="article">
<meta property="og:title" content="SurfaceView 与 TextureView 详解">
<meta property="og:url" content="https://xu-genyin.gitee.io/2024/01/24/android/SurfaceViewAndTextureView/">
<meta property="og:site_name" content="wiki小站 生活-代码之道">
<meta property="og:description" content="前言播放视频或者渲染其他的动画的时候，有两个 View 组件可供选择，SurfaceView 和 TextureView，GLSurfaceView 是 SurfaceView 的子类，在 SurfaceView 基础上封装了 EGL 环境管理以及 Render 线程，专用于 3D 游戏开发的视图，这里归类到 SurfaceView 中。在介绍 SurfaceView 与 TextureView">
<meta property="og:locale" content="zh_CN">
<meta property="article:published_time" content="2024-01-23T16:00:00.000Z">
<meta property="article:modified_time" content="2024-01-24T03:20:39.499Z">
<meta property="article:author" content="树根">
<meta property="article:tag" content="安卓开发">
<meta name="twitter:card" content="summary">


<link rel="icon" href="/img/avatar.jpg">

<link href="/css/style.css?v=1.1.0" rel="stylesheet">

<link href="/css/hl_theme/github.css?v=1.1.0" rel="stylesheet">

<link href="//cdn.jsdelivr.net/npm/animate.css@4.1.0/animate.min.css" rel="stylesheet">

<link href="//cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.css" rel="stylesheet">
<link href="//cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.min.css" rel="stylesheet">

<script src="//cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js"></script>
<script src="/js/titleTip.js?v=1.1.0" ></script>

<script src="//cdn.jsdelivr.net/npm/highlightjs@9.16.2/highlight.pack.min.js"></script>
<script>
    hljs.initHighlightingOnLoad();
</script>

<script src="//cdn.jsdelivr.net/npm/nprogress@0.2.0/nprogress.min.js"></script>



<script src="//cdn.jsdelivr.net/npm/jquery.cookie@1.4.1/jquery.cookie.min.js" ></script>

<script src="/js/iconfont.js?v=1.1.0" ></script>

<meta name="generator" content="Hexo 5.4.2"></head>
<div style="display: none">
  <input class="theme_disqus_on" value="false">
  <input class="theme_preload_comment" value="">
  <input class="theme_blog_path" value="">
  <input id="theme_shortcut" value="true" />
  <input id="theme_highlight_on" value="true" />
  <input id="theme_code_copy" value="true" />
</div>



<body>
<aside class="nav">
    <div class="nav-left">
        <a href="/"
   class="avatar_target">
    <img class="avatar"
         src="/img/avatar.jpg"/>
</a>
<div class="author">
    <span>树根</span>
</div>

<div class="icon">
    
        
            <a title="email"
               href="mailto:xuxian.xgy@gmail.com"
               target="_blank">
                
                    <svg class="iconfont-svg" aria-hidden="true">
                        <use xlink:href="#icon-email"></use>
                    </svg>
                
            </a>
        
    
        
            <a title="qq"
               href="http://wpa.qq.com/msgrd?v=3&uin=553503639&site=qq&menu=yes"
               target="_blank">
                
                    <svg class="iconfont-svg" aria-hidden="true">
                        <use xlink:href="#icon-qq"></use>
                    </svg>
                
            </a>
        
    
</div>





<ul>
    <li>
        <div class="all active" data-rel="全部文章">全部文章
            
                <small>(10)</small>
            
        </div>
    </li>
    
        
            
                
    <li>
        <div data-rel="安卓开发">
            
            安卓开发
            <small>(3)</small>
        </div>
        
    </li>

            
        
    
        
            
                
    <li>
        <div data-rel="博客的故事">
            
            博客的故事
            <small>(2)</small>
        </div>
        
    </li>

            
        
    
        
            
                
    <li>
        <div data-rel="chromium">
            
            chromium
            <small>(1)</small>
        </div>
        
    </li>

            
        
    
        
            
                
    <li>
        <div data-rel="git">
            
            git
            <small>(1)</small>
        </div>
        
    </li>

            
        
    
        
            
                
    <li>
        <div data-rel="TreeNet">
            
            TreeNet
            <small>(1)</small>
        </div>
        
    </li>

            
        
    
        
            
                
    <li>
        <div data-rel="typescript">
            
            typescript
            <small>(2)</small>
        </div>
        
    </li>

            
        
    
</ul>
<div class="left-bottom">
    <div class="menus">
        
    </div>
    <div>
        
        <a 
                                           class="friends">友链</a>
        
    </div>
</div>
<input type="hidden" id="yelog_site_posts_number" value="10">
<input type="hidden" id="yelog_site_word_count" value="7.6k">
<div style="display: none">
    <span id="busuanzi_value_site_uv"></span>
    <span id="busuanzi_value_site_pv"></span>
</div>

    </div>
    <div class="nav-right">
        <div class="friends-area">
    <div class="friends-title">
        友情链接
        <i class="iconfont icon-left"></i>
    </div>
    <div class="friends-content">
        <ul>
            
            <li><a target="_blank" href="https://gitee.com/xu-genyin/">gitee</a></li>
            
            <li><a target="_blank" href="http://yelog.org/">xx</a></li>
            
        </ul>
    </div>
</div>
        <div class="title-list">
    <div class="right-top">
        <div id="default-panel">
            <i class="iconfont icon-search" data-title="搜索 快捷键 i"></i>
            <div class="right-title">全部文章</div>
            <i class="iconfont icon-file-tree" data-title="切换到大纲视图 快捷键 w"></i>
        </div>
        <div id="search-panel">
            <i class="iconfont icon-left" data-title="返回"></i>
            <input id="local-search-input" autocomplete="off"/>
            <label class="border-line" for="input"></label>
            <i class="iconfont icon-case-sensitive" data-title="大小写敏感"></i>
            <i class="iconfont icon-tag" data-title="标签"></i>
        </div>
        <div id="outline-panel" style="display: none">
            <div class="right-title">大纲</div>
            <i class="iconfont icon-list" data-title="切换到文章列表"></i>
        </div>
    </div>

    <div class="tags-list">
    <input id="tag-search" />
    <div class="tag-wrapper">
        
            <li class="article-tag-list-item">
                <i class="iconfont icon-tag"></i><a>安卓开发</a>
            </li>
        
            <li class="article-tag-list-item">
                <i class="iconfont icon-tag"></i><a>搭建本站</a>
            </li>
        
            <li class="article-tag-list-item">
                <i class="iconfont icon-tag"></i><a>chromium</a>
            </li>
        
            <li class="article-tag-list-item">
                <i class="iconfont icon-tag"></i><a>git使用</a>
            </li>
        
            <li class="article-tag-list-item">
                <i class="iconfont icon-tag"></i><a>TreeNet</a>
            </li>
        
            <li class="article-tag-list-item">
                <i class="iconfont icon-tag"></i><a>ts</a>
            </li>
        
            <li class="article-tag-list-item">
                <i class="iconfont icon-tag"></i><a>ts的扩展</a>
            </li>
        
    </div>

</div>

    
    <div id="local-search-result">

    </div>
    
    <nav id="title-list-nav">
        
        
        <a  class="全部文章 安卓开发 "
           href="/2024/01/24/android/SurfaceViewAndTextureView/"
           data-tag="安卓开发"
           data-author="treeroot" >
            <span class="post-title" title="SurfaceView 与 TextureView 详解">SurfaceView 与 TextureView 详解</span>
            <span class="post-date" title="2024-01-24 00:00:00">2024/01/24</span>
        </a>
        
        
        <a  class="全部文章 chromium "
           href="/2023/11/23/chromium/core/"
           data-tag="chromium"
           data-author="treeroot" >
            <span class="post-title" title="【chromium内核笔记】源码结构篇">【chromium内核笔记】源码结构篇</span>
            <span class="post-date" title="2023-11-23 08:46:00">2023/11/23</span>
        </a>
        
        
        <a  class="全部文章 安卓开发 "
           href="/2023/11/14/android/okhttp/"
           data-tag="安卓开发"
           data-author="小广～" >
            <span class="post-title" title="OkHttp">OkHttp</span>
            <span class="post-date" title="2023-11-14 14:21:00">2023/11/14</span>
        </a>
        
        
        <a  class="全部文章 TreeNet "
           href="/2023/11/03/treenet/instruction/"
           data-tag="TreeNet"
           data-author="" >
            <span class="post-title" title="开源网关项目：TreeNet">开源网关项目：TreeNet</span>
            <span class="post-date" title="2023-11-03 16:15:54">2023/11/03</span>
        </a>
        
        
        <a  class="全部文章 typescript "
           href="/2023/10/27/typescript/tsExtends/"
           data-tag="ts的扩展"
           data-author="" >
            <span class="post-title" title="ts的扩展信息">ts的扩展信息</span>
            <span class="post-date" title="2023-10-27 11:15:54">2023/10/27</span>
        </a>
        
        
        <a  class="全部文章 typescript "
           href="/2023/10/27/typescript/whyToUse/"
           data-tag="ts"
           data-author="" >
            <span class="post-title" title="为什么要使用typeScript">为什么要使用typeScript</span>
            <span class="post-date" title="2023-10-27 11:15:54">2023/10/27</span>
        </a>
        
        
        <a  class="全部文章 git "
           href="/2023/10/27/git/useGit/"
           data-tag="git使用"
           data-author="" >
            <span class="post-title" title="使用git">使用git</span>
            <span class="post-date" title="2023-10-27 08:38:54">2023/10/27</span>
        </a>
        
        
        <a  class="全部文章 安卓开发 "
           href="/2023/10/25/android/activityAndFragment/"
           data-tag="安卓开发"
           data-author="小广～" >
            <span class="post-title" title="Activity和Fragment生命周期">Activity和Fragment生命周期</span>
            <span class="post-date" title="2023-10-25 15:52:14">2023/10/25</span>
        </a>
        
        
        <a  class="全部文章 博客的故事 "
           href="/2023/10/25/blog/toHaveSameSite/"
           data-tag="搭建本站"
           data-author="" >
            <span class="post-title" title="如何去搭建同款本站">如何去搭建同款本站</span>
            <span class="post-date" title="2023-10-25 08:54:14">2023/10/25</span>
        </a>
        
        
        <a  class="全部文章 博客的故事 "
           href="/2023/10/23/blog/start/"
           data-tag=""
           data-author="" >
            <span class="post-title" title="关于这个小破站">关于这个小破站</span>
            <span class="post-date" title="2023-10-23 22:04:14">2023/10/23</span>
        </a>
        
        <div id="no-item-tips">

        </div>
    </nav>
    <div id="outline-list">
    </div>
</div>

    </div>
    <div class="hide-list">
        <div class="semicircle" data-title="切换全屏 快捷键 s">
            <div class="brackets first"><</div>
            <div class="brackets">&gt;</div>
        </div>
    </div>
</aside>
<div id="post">
    <div class="pjax">
        <article id="post-android/SurfaceViewAndTextureView" class="article article-type-post" itemscope itemprop="blogPost">
    
        <h1 class="article-title">SurfaceView 与 TextureView 详解</h1>
    
    <div class="article-meta">
        
        
        <span class="author"><a>treeroot</a></span>
        
        
        <span class="book">
            <i class="iconfont icon-category"></i>
            
            
            <a  data-rel="安卓开发">安卓开发</a>
            
        </span>
        
        
        <span class="tag">
            <i class="iconfont icon-tag"></i>
            
            <a class="color5">安卓开发</a>
            
        </span>
        
    </div>
    <div class="article-meta">
        
            发布时间 : <time class="date" title='最后更新: 2024-01-24 11:20:39'>2024-01-24 00:00</time>
        
    </div>
    <div class="article-meta">
        
        <span>字数:2.3k</span>
        
        
        <span id="busuanzi_container_page_pv">
            阅读 :<span id="busuanzi_value_page_pv">
                <span class="count-comment">
                    <span class="spinner">
                      <div class="cube1"></div>
                      <div class="cube2"></div>
                    </span>
                </span>
            </span>
        </span>
        
        
    </div>
    
    <div class="toc-ref">
    
        <ol class="toc"><li class="toc-item toc-level-1"><a class="toc-link" href="#%E5%89%8D%E8%A8%80"><span class="toc-text">前言</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#1-Surface"><span class="toc-text">1 Surface</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#2-SurfaceView"><span class="toc-text">2 SurfaceView</span></a><ol class="toc-child"><li class="toc-item toc-level-2"><a class="toc-link" href="#2-1-SurfaceView-%E7%AE%80%E4%BB%8B"><span class="toc-text">2.1 SurfaceView 简介</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#2-2-SurfaceView-%E5%AE%9E%E7%8E%B0%E6%9C%BA%E5%88%B6"><span class="toc-text">2.2 SurfaceView 实现机制</span></a></li></ol></li><li class="toc-item toc-level-1"><a class="toc-link" href="#3-TextureView"><span class="toc-text">3 TextureView</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#4-SurfaceTexture"><span class="toc-text">4 SurfaceTexture</span></a></li><li class="toc-item toc-level-1"><a class="toc-link" href="#5-SurfaceView-%E4%B8%8E-TextureView-%E7%9A%84%E5%AF%B9%E6%AF%94"><span class="toc-text">5 SurfaceView 与 TextureView 的对比</span></a></li></ol>
    
<style>
    .left-col .switch-btn,
    .left-col .switch-area {
        display: none;
    }
    .toc-level-4 i,
    .toc-level-4 ol {
        display: none !important;
    }
</style>
</div>

    
    <div class="article-entry" itemprop="articleBody">
      
        <h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>播放视频或者渲染其他的动画的时候，有两个 View 组件可供选择，SurfaceView 和 TextureView，GLSurfaceView 是 SurfaceView 的子类，在 SurfaceView 基础上封装了 EGL 环境管理以及 Render 线程，专用于 3D 游戏开发的视图，这里归类到 SurfaceView 中。在介绍 SurfaceView 与 TextureView 之前，需要先介绍下 Surface。</p>
<h1 id="1-Surface"><a href="#1-Surface" class="headerlink" title="1 Surface"></a>1 Surface</h1><p>Surface 就是“表面”的意思，可以<strong>简单理解为内存中的一段绘图缓冲区</strong>。在 SDK 的文档中，对 Surface 的描述是这样的：“Handle onto a raw buffer that is being managed by the screen compositor”，意思是“由屏幕显示内容合成器（screen compositor）所管理的原生缓冲器的句柄”， 这句话包括下面两个意思：</p>
<ul>
<li>通过 Surface（因为 Surface 是句柄）就可以获得原生缓冲器以及其中的内容。就像在C语言中，可以通过一个文件的句柄，就可以获得文件的内容一样；</li>
<li>原生缓冲器（rawbuffer）是用于保存当前窗口的像素数据的。</li>
</ul>
<p>简单的说 Surface 对应了一块屏幕缓冲区，每个 Window 对应一个 Surface，任何 View 都是画在 Surface 上的，传统的 view 共享一块屏幕缓冲区，所有的绘制必须在 UI 线程中进行。我们不能直接操作 Surface 实例，要通过 SurfaceHolder，在 SurfaceView 中可以通过 getHolder() 方法获取到 SurfaceHolder 实例。 Surface 是一个用来画图形的地方，但是我们知道画图都是在一个 Canvas 对象上面进行的，Surface 中的 Canvas 成员，是专门用于提供画图的地方，就像黑板一样，其中的原始缓冲区是用来保存数据的地方。 Surface 本身的作用类似一个句柄，得到了这个句柄就可以得到其中的Canvas、原始缓冲区以及其他方面的内容，所以简单的说 Surface 是用来管理数据的（句柄）。 说完 surface 就可以说 SurfaceView 了。</p>
<h1 id="2-SurfaceView"><a href="#2-SurfaceView" class="headerlink" title="2 SurfaceView"></a>2 SurfaceView</h1><h2 id="2-1-SurfaceView-简介"><a href="#2-1-SurfaceView-简介" class="headerlink" title="2.1 SurfaceView 简介"></a>2.1 SurfaceView 简介</h2><p>简单的说 SurfaceView 就是一个有 Surface 的 View，SurfaceView 控制这个 Surface 的格式和尺寸以及绘制位置。传统 View 及其派生类的更新只能在 UI 线程，然而 UI 线程还同时处理其他交互逻辑，这就无法保证 view 更新的速度和帧率了，而 SurfaceView 可以用独立的线程来进行绘制。因此可以提供更高的帧率，例如游戏，摄像头取景等场景就比较适合用 SurfaceView 来实现。 <strong>SurfaceView 的核心在于提供了两个线程：UI线程和渲染线程，两个线程通过“双缓冲”机制来达到高效的界面刷新效果。</strong> 不用画布，直接在窗口上进行绘图叫做无缓冲绘图。用了一个画布，将所有内容都先画到画布上，在整体绘制到窗口上，就该叫做单缓冲绘图，那个画布就是一个缓冲区。用了两个画布，一个进行临时的绘图，一个进行最终的绘图，这样就叫做双缓冲。 而这个双缓冲可以理解为，SurfaceView 在更新视图时用到了两张 Canvas：</p>
<ul>
<li>frontCanvas：实际显示的canvas。</li>
<li>backCanvas：存储的是上一次更改前的canvas。</li>
</ul>
<p>当使用 lockCanvas() 获取画布时，得到的实际上是 backCanvas 而不是正在显示的 frontCanvas，之后你在获取到的 backCanvas 上绘制新视图，再 unlockCanvasAndPost（canvas）此视图，那么上传的这张 canvas 将替换原来的 frontCanvas 作为新的 frontCanvas，原来的 frontCanvas 将切换到后台作为 backCanvas。</p>
<h2 id="2-2-SurfaceView-实现机制"><a href="#2-2-SurfaceView-实现机制" class="headerlink" title="2.2 SurfaceView 实现机制"></a>2.2 SurfaceView 实现机制</h2><p>SurfaceView 继承自 View，所以它也是一个 View。但是这个 View 和普通的 View 有点不同。<strong>SurfaceView 有自己的 Surface</strong>，在 Android 中，一个 View 有自己的 Surface，在 WMS 中中就有对应的 WindowState，对应在 SurfaceFlinger 中就有 Layer。</p>
<p>一般的 Activity 包含的多个 View 会组成 View hierachy 的树形结构，只有最顶层的 DectorView 才是对 WMS 可见的，这个 DecorView 在 WMS 中有一个对应的 WindowState，相应的，在 SurfaceFlinger 中有对应的 Layer。而 SurfaceView 正因为它有自己的 Surface，有自己的 Window，它在 WMS 中有对应的 WindowState，在 SurfaceFlinger 中有 Layer。 虽然在 App 端它仍在 View hierachy 中，但在 Server 端（WMS 和 SurfaceFlinger）中，它与宿主窗口是分离的。这样的好处是对这个 Surface 的渲染可以放到单独的线程中去做，渲染时可以有自己的 GL context。这对于一些游戏、视频等性能相关的应用非常有益，因为它不会影响主线程对事件的响应。 但是这也有缺点，因为这个 Surface 不在 View hierachy 中，它的显示也不受 View 的属性控制，所以不能进行平移、缩放等动画，它也不能放在其它 ViewGroup 中，SurfaceView 不能嵌套使用，而且不能使用某些 View 的特性，例如 View.setAlpha()。 从 Android7.0 开始，SurfaceView 的窗口位置与其他 View 渲染同步更新。这意味着在屏幕上平移和缩放 SurfaceView 不会导致渲染失真。</p>
<h1 id="3-TextureView"><a href="#3-TextureView" class="headerlink" title="3 TextureView"></a>3 TextureView</h1><p>因为上面所说的 SurfaceView 不在主窗口中，它没法做动画没法使用一些 View 的特性方法，所以<strong>在 Android 4.0中引入了 TextureView，它是一个结合了 View 和 SurfaceTexture 的 View 对象</strong>。 TextureView <strong>是一个可以把内容流作为外部纹理输出在上面的 View</strong>，和 SurfaceView 不同，它不会在 WMS 中单独创建窗口，而是作为 View hierachy 中的一个普通 view，<strong>因此它可以和其他普通 View 一样进行平移、旋转、缩放等动画</strong>。但是 TextureView 必须在硬件加速的窗口中，它显示的内容流数据可以来自 App 进程或者远程进程。 TextureView 继承自 View，它与其它的 View 一样在 View hierachy 中管理与绘制。TextureView 重载了 draw() 方法，其中主要 SurfaceTexture 中收到的图像数据作为纹理更新到对应的 HardwareLayer 中。 SurfaceTexture.OnFrameAvailableListener 用于通知 TextureView 内容流有新图像到来。SurfaceTextureListener 接口用于让 TextureView 的使用者知道 SurfaceTexture 已准备好，这样就可以把 SurfaceTexture 交给相应的内容源。 Surface 为 BufferQueue 的 Producer 接口实现类，使生产者可以通过它的软件或硬件渲染接口为 SurfaceTexture 内部的 BufferQueue 提供 graphic buffer。 SurfaceTexture 可以用作非直接输出的内容流，这样就提供二次处理的机会。与 SurfaceView 直接输出相比，这样会有若干帧的延迟。同时，由于它本身管理 BufferQueue，因此内存消耗也会稍微大一些。</p>
<h1 id="4-SurfaceTexture"><a href="#4-SurfaceTexture" class="headerlink" title="4 SurfaceTexture"></a>4 SurfaceTexture</h1><p><strong>SurfaceTexture 是 Surface 和 OpenGL ES(GLES) 纹理的组合。SurfaceTexture 用于提供输出到 GLES 纹理的 Surface。</strong> SurfaceTexture 是从 Android 3.0(API level 11)开始加入，与 SurfaceView 不同的是，<strong>它对图像流的处理并不直接显示，而是转为 GL 外部纹理，因此用于图像流数据的二次处理</strong>。 比如 Camera 的预览数据，变成纹理后可以交给 GLSurfaceView 直接显示，也可以通过 SurfaceTexture 交给TextureView 作为 View heirachy 中的一个硬件加速层来显示。 首先，SurfaceTexture 从图像流 （来自 Camera 预览、视频解码、GL 绘制场景等）中获得帧数据，当调用updateTexImage()时，根据内容流中最近的图像更新 SurfaceTexture 对应的 GL 纹理对象。 SurfaceTexture 包含一个应用是其使用方的 BufferQueue。当生产方将新的缓冲区排入队列时，onFrameAvailable() 回调会通知应用。然后，应用调用 updateTexImage()，这会释放先前占有的缓冲区，从队列中获取新缓冲区并执行 EGL 调用，从而使 GLES 可将此缓冲区作为外部纹理使用。</p>
<h1 id="5-SurfaceView-与-TextureView-的对比"><a href="#5-SurfaceView-与-TextureView-的对比" class="headerlink" title="5 SurfaceView 与 TextureView 的对比"></a>5 SurfaceView 与 TextureView 的对比</h1><table>
<thead>
<tr>
<th>项目</th>
<th>SurfaceView</th>
<th>TextureView</th>
</tr>
</thead>
<tbody><tr>
<td>内存</td>
<td>低</td>
<td>高</td>
</tr>
<tr>
<td>耗电</td>
<td>低</td>
<td>高</td>
</tr>
<tr>
<td>绘制</td>
<td>及时</td>
<td>1-3帧延迟</td>
</tr>
<tr>
<td>动画和截图</td>
<td>不支持</td>
<td>支持</td>
</tr>
</tbody></table>
<p>从性能和安全性角度出发，<strong>优先选 SurfaceView，TextureView 是一个不得已的选择</strong>：</p>
<ul>
<li><strong>在 Android 7.0 上系统 Surfaceview 的性能比 TextureView 更有优势</strong>，支持对象的内容位置和包含的应用内容同步更新，平移、缩放不会产生黑边。 <strong>在7.0以下系统如果使用场景有动画效果，可以选择性使用TextureView</strong>。</li>
<li>由于失效（invalidation）和缓冲的特性，<strong>TextureView 增加了额外1~3帧的延迟显示画面更新</strong>。</li>
<li>TextureView 总是使用 GL 合成，而 SurfaceView 可以使用硬件 overlay 后端，可以占用更少的内存。</li>
<li>TextureView 的内部缓冲队列导致比 SurfaceView 使用更多的内存。</li>
<li>SurfaceView 内部自己持有 Surface，Surface 创建、销毁、大小改变时系统来处理的，通过 SurfaceHolder  的 callback 回调通知。</li>
<li>当画布创建好时，可以将 surface 绑定到 MediaPlayer 中。SurfaceView 如果为用户可见的时候，创建 SurfaceView 的 SurfaceHolder 用于显示视频流解析的帧图片，如果发现 SurfaceView 变为用户不可见的时候，则立即销毁 SurfaceView 的 SurfaceHolder，以达到节约系统资源的目的。</li>
</ul>

      
       <hr><span style="font-style: italic;color: gray;"> 欢迎指出任何有错误或不够清晰的表达。也可以邮件至 xuxian.xgy@gmail.com </span>
    </div>
</article>


<p>
    <a  class="dashang" onclick="dashangToggle()">赏</a>
</p>


<div class="article_copyright">
    <p><span class="copy-title">文章标题:</span>SurfaceView 与 TextureView 详解</p>
    <p><span class="copy-title">字数:</span><span class="post-count">2.3k</span></p>
    <p><span class="copy-title">本文作者:</span><a  title="树根">树根</a></p>
    <p><span class="copy-title">发布时间:</span>2024-01-24, 00:00:00</p>
    <p><span class="copy-title">最后更新:</span>2024-01-24, 11:20:39</p>
    <span class="copy-title">原始链接:</span><a class="post-url" href="/2024/01/24/android/SurfaceViewAndTextureView/" title="SurfaceView 与 TextureView 详解">https://xu-genyin.gitee.io/2024/01/24/android/SurfaceViewAndTextureView/</a>
    <p>
        <span class="copy-title">版权声明:</span><i class="fa fa-creative-commons"></i> <a rel="license noopener" target="_blank" href="http://creativecommons.org/licenses/by-nc-sa/4.0/" title="CC BY-NC-SA 4.0 International" target = "_blank">&#34;署名-非商用-相同方式共享 4.0&#34;</a> 转载请保留原文链接及作者。
    </p>
</div>





    




    </div>
    <div class="copyright">
        <p class="footer-entry">
    git，记录程序员美好生活～欢迎投稿 邮件至 xuxian.xgy@gmail.com
</p>
<p class="footer-entry">Built with <a href="https://hexo.io/" target="_blank">Hexo</a> and <a href="https://github.com/yelog/hexo-theme-3-hexo" target="_blank">3-hexo</a> theme</p>

    </div>
    <div class="full-toc">
        <button class="full" data-title="切换全屏 快捷键 s"><span class="min "></span></button>
<a class="" id="rocket" ></a>

    </div>
</div>

<div class="hide_box" onclick="dashangToggle()"></div>
<div class="shang_box">
    <a class="shang_close"  onclick="dashangToggle()">×</a>
    <div class="shang_tit">
        <p>看官，赏个咖啡～</p>
    </div>
    <div class="shang_payimg">
        <div class="pay_img">
            <img src="/img/alipay.jpg" class="alipay" title="扫码支持">
            <img src="/img/weixin.jpg" class="weixin" title="扫码支持">
        </div>
    </div>
    <div class="shang_payselect">
        <span><label><input type="radio" name="pay" checked value="alipay">支付宝</label></span><span><label><input type="radio" name="pay" value="weixin">微信</label></span>
    </div>
</div>


</body>
<script src="/js/jquery.pjax.js?v=1.1.0" ></script>

<script src="//cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js"></script>
<script src="//cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js"></script>

<script src="/js/script.js?v=1.1.0" ></script>
<script>
    var img_resize = 'photoSwipe';
    function initArticle() {
        /*渲染对应的表格样式*/
        
            $("#post .pjax table").addClass("green_title");
        

        /*渲染打赏样式*/
        
        $("input[name=pay]").on("click", function () {
            if($("input[name=pay]:checked").val()=="weixin"){
                $(".shang_box .shang_payimg .pay_img").addClass("weixin_img");
            } else {
                $(".shang_box .shang_payimg .pay_img").removeClass("weixin_img");
            }
        })
        

        /*高亮代码块行号*/
        

        /*访问数量*/
        
        $.getScript("//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js");
        

        /*代码高亮，行号对齐*/
        $('.pre-numbering').css('line-height',$('.has-numbering').css('line-height'));

        

        // PhotoSwipe
        $('article').each(function(i){
            $(this).find('img').each(function(){
                if ($(this).closest('figure').hasClass('article-gallery-img')) {
                    return;
                }
                var alt = this.alt;
                $(this)
                    .wrap('<figure class="article-gallery-img" itemprop="associatedMedia" itemscope itemtype="http://schema.org/ImageObject"></figure>')
                    .wrap('<a href="' + this.src + '" title="' + alt + '"></a>');
                $(this).after('<div class="img_alt"><span>' + (alt || '') + '</span></div>');
            });
        });

        var pswpElement = document.querySelectorAll('.pswp')[0];
        if (pswpElement) {
            var gallerySelector = '.article-gallery, article';

            var initPhotoSwipeFromDOM = function(gallerySelector) {

                // parse slide data (url, title, size ...) from DOM elements
                // (children of gallerySelector)
                var parseThumbnailElements = function(el) {
                    var thumbElements = $(el).find('figure.article-gallery-img').toArray(),
                        numNodes = thumbElements.length,
                        items = [],
                        figureEl,
                        linkEl,
                        size,
                        imgEl,
                        item;

                    for (var i = 0; i < numNodes; i++) {

                        figureEl = thumbElements[i]; // <figure> element

                        // include only element nodes
                        if (figureEl.nodeType !== 1) {
                            continue;
                        }

                        linkEl = figureEl.children[0]; // <a> element
                        imgEl = linkEl.children[0]; // <img>

                        size = linkEl.getAttribute('data-size');
                        size = size && size.split('x');

                        // create slide object
                        item = {
                            src: linkEl.getAttribute('href'),
                            w: size && parseInt(size[0], 10) || imgEl.width,
                            h: size && parseInt(size[1], 10) || imgEl.height
                        };

                        if (figureEl.children.length > 1) {
                            // <figcaption> content
                            item.title = figureEl.children[1].innerHTML;
                        }

                        if (linkEl.children.length > 0) {
                            // <img> thumbnail element, retrieving thumbnail url
                            item.msrc = linkEl.children[0].getAttribute('src');
                        }

                        item.el = figureEl; // save link to element for getThumbBoundsFn
                        items.push(item);
                    }

                    return items;
                };

                // find nearest parent element
                var closest = function closest(el, fn) {
                    return el && (fn(el) ? el : closest(el.parentNode, fn));
                };

                // triggers when user clicks on thumbnail
                var onThumbnailsClick = function(e) {
                    e = e || window.event;

                    var eTarget = e.target || e.srcElement;

                    // find root element of slide
                    var clickedListItem = closest(eTarget, function(el) {
                        return (el.tagName && el.tagName.toUpperCase() === 'FIGURE');
                    });

                    if (!clickedListItem) {
                        return;
                    }

                    if (e.preventDefault) {
                        e.preventDefault();
                    } else {
                        e.returnValue = false;
                    }

                    // find index of clicked item by looping through all child nodes
                    // alternatively, you may define index via data- attribute
                    var clickedGallery = $(clickedListItem).closest(gallerySelector)[0],
                        childNodes = $(clickedGallery).find('figure.article-gallery-img').toArray(),
                        numChildNodes = childNodes.length,
                        nodeIndex = 0,
                        index;

                    for (var i = 0; i < numChildNodes; i++) {
                        if (childNodes[i].nodeType !== 1) {
                            continue;
                        }

                        if (childNodes[i] === clickedListItem) {
                            index = nodeIndex;
                            break;
                        }
                        nodeIndex++;
                    }



                    if (index >= 0) {
                        // open PhotoSwipe if valid index found
                        openPhotoSwipe(index, clickedGallery);
                    }
                    return false;
                };

                // parse picture index and gallery index from URL (#&pid=1&gid=2)
                var photoswipeParseHash = function() {
                    var hash = window.location.hash.substring(1),
                        params = {};

                    if (hash.length < 5) {
                        return params;
                    }

                    var vars = hash.split('&');
                    for (var i = 0; i < vars.length; i++) {
                        if (!vars[i]) {
                            continue;
                        }
                        var pair = vars[i].split('=');
                        if (pair.length < 2) {
                            continue;
                        }
                        params[pair[0]] = pair[1];
                    }

                    if (params.gid) {
                        params.gid = parseInt(params.gid, 10);
                    }

                    return params;
                };

                var openPhotoSwipe = function(index, galleryElement, disableAnimation, fromURL) {
                    var pswpElement = document.querySelectorAll('.pswp')[0],
                        gallery,
                        options,
                        items;

                    items = parseThumbnailElements(galleryElement);

                    // define options (if needed)
                    options = {

                        // define gallery index (for URL)
                        galleryUID: galleryElement.getAttribute('data-pswp-uid'),

                        getThumbBoundsFn: function(index) {
                            // See Options -> getThumbBoundsFn section of documentation for more info
                            var thumbnail = items[index].el.getElementsByTagName('img')[0], // find thumbnail
                                pageYScroll = window.pageYOffset || document.documentElement.scrollTop,
                                rect = thumbnail.getBoundingClientRect();

                            return {
                                x: rect.left,
                                y: rect.top + pageYScroll,
                                w: rect.width
                            };
                        }
                    };

                    // PhotoSwipe opened from URL
                    if (fromURL) {
                        if (options.galleryPIDs) {
                            // parse real index when custom PIDs are used
                            // http://photoswipe.com/documentation/faq.html#custom-pid-in-url
                            for (var j = 0; j < items.length; j++) {
                                if (items[j].pid == index) {
                                    options.index = j;
                                    break;
                                }
                            }
                        } else {
                            // in URL indexes start from 1
                            options.index = parseInt(index, 10) - 1;
                        }
                    } else {
                        options.index = parseInt(index, 10);
                    }

                    // exit if index not found
                    if (isNaN(options.index)) {
                        return;
                    }

                    if (disableAnimation) {
                        options.showAnimationDuration = 0;
                    }

                    // Pass data to PhotoSwipe and initialize it
                    gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options);

                    gallery.listen('imageLoadComplete', function(index, item) {
                        var linkEl = item.el.children[0];
                        var img = item.container.children[0];
                        if (!linkEl.getAttribute('data-size')) {
                            linkEl.setAttribute('data-size', img.naturalWidth + 'x' + img.naturalHeight);
                            item.w = img.naturalWidth;
                            item.h = img.naturalHeight;
                            gallery.invalidateCurrItems();
                            gallery.updateSize(true);
                        }
                    });

                    gallery.init();
                };

                // loop through all gallery elements and bind events
                var galleryElements = document.querySelectorAll(gallerySelector);

                for (var i = 0, l = galleryElements.length; i < l; i++) {
                    galleryElements[i].setAttribute('data-pswp-uid', i + 1);
                    galleryElements[i].onclick = onThumbnailsClick;
                }

                // Parse URL and open gallery if it contains #&pid=3&gid=1
                var hashData = photoswipeParseHash();
                if (hashData.pid && hashData.gid) {
                    openPhotoSwipe(hashData.pid, galleryElements[hashData.gid - 1], true, true);
                }
            };

            // execute above function
            initPhotoSwipeFromDOM(gallerySelector);
        }
        
        
    }

    /*打赏页面隐藏与展示*/
    
    function dashangToggle() {
        $(".shang_box").fadeToggle();
        $(".hide_box").fadeToggle();
    }
    

</script>

<!--加入行号的高亮代码块样式-->

<!--自定义样式设置-->
<style>
    
    
    .nav {
        width: 542px;
    }
    .nav.fullscreen {
        margin-left: -542px;
    }
    .nav-left {
        width: 120px;
    }
    
    
    @media screen and (max-width: 1468px) {
        .nav {
            width: 492px;
        }
        .nav.fullscreen {
            margin-left: -492px;
        }
        .nav-left {
            width: 100px;
        }
    }
    
    
    @media screen and (max-width: 1024px) {
        .nav {
            width: 492px;
            margin-left: -492px
        }
        .nav.fullscreen {
            margin-left: 0;
        }
    }
    
    @media screen and (max-width: 426px) {
        .nav {
            width: 100%;
        }
        .nav-left {
            width: 100%;
        }
    }
    
    
    .nav-right .title-list nav a .post-title, .nav-right .title-list #local-search-result a .post-title {
        color: #383636;
    }
    
    
    .nav-right .title-list nav a .post-date, .nav-right .title-list #local-search-result a .post-date {
        color: #5e5e5f;
    }
    
    
    .nav-right nav a.hover, #local-search-result a.hover{
        background-color: #e2e0e0;
    }
    
    

    /*列表样式*/
    

    /* 背景图样式 */
    
    #post {
        background: url(https://guli-parent-tree-root.oss-cn-hangzhou.aliyuncs.com/2023%3A10%3A23%3Awallpaper1680173153576.jpg);
    }
    
    


    /*引用块样式*/
    

    /*文章列表背景图*/
    

    
    #post .pjax article :not(pre) > code {
        color: #24292e;
        font-family: SFMono-Regular,Consolas,Liberation Mono,Menlo,Courier,monospace;
        background-color: rgba(27,31,35,.05);
        border-radius: 3px;
        font-size: 85%;
        margin: 0;
        padding: .2em .4em;
    }
    
</style>


<!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe.
         It's a separate element, as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. PhotoSwipe keeps only 3 slides in DOM to save memory. -->
        <!-- don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <!-- Preloader demo https://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                        <div class="pswp__preloader__cut">
                            <div class="pswp__preloader__donut"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div>
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>






</html>
